#ifndef _SMTPCLIENTSESSION_HPP_
#define _SMTPCLIENTSESSION_HPP_

#include "jhdefs.hpp"
#include "net/StreamSocket.hpp"
#include "net/MailMessage.hpp"
#include "net/DialogSocket.hpp"
#include "stdlib/timespan.hpp"
using JHCPP::stdlib::CTimeSpan;
#include "code/digestengine.hpp"
using namespace JHCPP::code;
#include <string>
#include <vector>

NET_NAMESPACE_BEGIN

class CSMTPClientSession
{
public:
	typedef std::vector<std::string> Recipients;

	enum
	{
		SMTP_PORT = 25
	};

	enum LoginMethod
	{
		AUTH_NONE,
		AUTH_CRAM_MD5,
		AUTH_CRAM_SHA1,
		AUTH_LOGIN,
		AUTH_PLAIN
	};

	explicit CSMTPClientSession(const CStreamSocket& socket);
	CSMTPClientSession(const std::string& host, UInt16 port = SMTP_PORT);
	virtual ~CSMTPClientSession();

	void setTimeout(const CTimeSpan& timeout);
	CTimeSpan getTimeout() const;

	void login(const std::string& hostname);
	void login();
	void login(const std::string& hostname, LoginMethod loginMethod, const std::string& username, const std::string& password);
	void login(LoginMethod loginMethod, const std::string& username, const std::string& password);
	void open();
	void close();

	void sendMessage(const CMailMessage& message);
	void sendMessage(const CMailMessage& message, const Recipients& recipients);
	void sendMessage(std::istream& istr);
	int sendCommand(const std::string& command, std::string& response);
	int sendCommand(const std::string& command, const std::string& arg, std::string& response);
	void sendAddresses(const std::string& from, const Recipients& recipients);
	void sendData();

protected:
	enum StatusClass
	{
		SMTP_POSITIVE_COMPLETION   = 2,
		SMTP_POSITIVE_INTERMEDIATE = 3,
		SMTP_TRANSIENT_NEGATIVE    = 4,
		SMTP_PERMANENT_NEGATIVE    = 5
	};
	enum
	{
		DEFAULT_TIMEOUT = 30000000 // 30 seconds default timeout for socket operations	
	};

	static bool isPositiveCompletion(int status);
	static bool isPositiveIntermediate(int status);
	static bool isTransientNegative(int status);
	static bool isPermanentNegative(int status);

	void login(const std::string& hostname, std::string& response);
	void loginUsingCRAMMD5(const std::string& username, const std::string& password);
	void loginUsingCRAMSHA1(const std::string& username, const std::string& password);
	void loginUsingCRAM(const std::string& username, const std::string& method, CDigestEngine& hmac);
	void loginUsingLogin(const std::string& username, const std::string& password);
	void loginUsingPlain(const std::string& username, const std::string& password);
	CDialogSocket& socket();

private:
	void sendCommands(const CMailMessage& message, const Recipients* pRecipients = 0);
	void transportMessage(const CMailMessage& message);

	CDialogSocket	m_socket;
	bool			m_isOpen;
};

NET_NAMESPACE_END

#endif
